package com.nekonomics
package domain.service.util

import config.JwtConfig
import error.TokenError
import error.TokenError.{InvalidToken, TokenExpired}

import pdi.jwt.*
import zio.*
import zio.json.*

import java.time.Instant
import scala.util.{Failure, Success}

trait TokenService {
  def generateToken(userId: Long): UIO[String]

  /**
   * 
   * @param token
   * @return 返回UserId
   */
  def verifyToken(token: String): IO[TokenError, Long] 
}

object TokenService {
  val live: TaskLayer[JwtTokenServiceLive] =
    config.layers >>> ZLayer.fromFunction((conf: JwtConfig) => JwtTokenServiceLive(conf.secretKey))
}


case class JwtPayload(userId: Long)derives JsonCodec


case class JwtTokenServiceLive(secretKey: String) extends TokenService {
  private val algo = JwtAlgorithm.HS256

  override def generateToken(userId: Long): UIO[String] = ZIO.succeed {
    val now = Instant.now.getEpochSecond
    val expire = now + constant.OneDayInSecond
    val claim = JwtClaim(
      content = JwtPayload(userId).toJson,
      issuedAt = Some(now),
      expiration = Some(expire)
    )
    Jwt.encode(claim, secretKey, algo)
  }

  override def verifyToken(token: String): IO[TokenError, Long] =
    Jwt.decode(token, secretKey, algo :: Nil) match
      case Success(claim) =>
        for {
          expire <- ZIO.fromEither(claim.expiration.toRight(InvalidToken("缺失expire字段")))
          payload <- {
            if expire < Instant.now.getEpochSecond then
              ZIO.fail(TokenExpired)
            else
              ZIO.fromEither(claim.content.fromJson[JwtPayload])
                .mapError(InvalidToken(_))
          }
        } yield payload.userId

      case Failure(err) => ZIO.fail(InvalidToken(err.getMessage))

}





















